package service

import (
	"context"
	"fmt"
	"github.com/pkg/errors"
	"gorm.io/gorm"
	"prod/api/prod"
	"prod/internal/conf"
	"prod/internal/constant"
	"prod/internal/model"
	"prod/pkg/ecode"
	"prod/pkg/request_key"
	"prod/pkg/util"
	"strconv"
	"strings"
	"time"
)

// 查看商品导入任务列表
func (s *Service) GetImportTaskList(ctx context.Context, req *prod.TaskListRequest) (resp *prod.TaskListResp, err error) {
	if err = s.dao.CheckPermission(ctx, req.Token, req.PStoreId); err != nil {
		return
	}
	resp = &prod.TaskListResp{Tasks: make([]*prod.TaskView, 0)}
	var tasks []*model.Task
	if tasks, resp.Total, err = s.dao.GetTaskList(ctx, req.Token.SgId, req.PStoreId, req.Nature,
		req.PageNo, req.PageSize); err != nil {
		return
	}
	for _, task := range tasks {
		resp.Tasks = append(resp.Tasks, &prod.TaskView{
			Id:         task.ID,
			Operator:   task.OperatorName,
			UploadTime: task.CreatedAt.Format(util.DefaultLayout),
			Status:     prod.TaskStatus(task.Status),
			Extra:      task.Extra,
		})
	}
	return
}

// 任务进度，传入任务ID
func (s *Service) GetTaskProgress(ctx context.Context, req *prod.CommonObjectRequest) (resp *prod.TaskProgress, err error) {
	if err = s.dao.CheckPermission(ctx, req.Token, req.PStoreId); err != nil {
		return
	}
	var progress int32
	if val, e := s.dao.RedisClient.Get(fmt.Sprintf(constant.TaskProgressKey, req.Id)).Int64(); e != nil {
		progress = -1
	} else {
		progress = int32(val)
	}
	resp = &prod.TaskProgress{Progress: progress}
	return
}

// 任务进度，传入任务ID
func (s *Service) ImportProd(ctx context.Context, req *prod.ImportProdRequest) (resp *prod.ImportTaskResp, err error) {
	if err = s.dao.CheckPermission(ctx, req.Token, req.PStoreId); err != nil {
		return
	}
	var data []map[string]string
	if data, err = s.dao.GetTaskExcel(req.Link); err != nil {
		return
	}
	task := &model.Task{
		Operator:     req.Token.EmpId,
		OperatorName: req.Token.EmpName,
		TaskType:     uint8(req.Nature),
		Link:         req.Link,
		Extra:        "",
		SgId:         req.Token.SgId,
		PStoreId:     req.PStoreId,
		Status:       0,
	}
	if err = s.dao.Create(ctx, task); err != nil {
		return
	}
	s.dao.RedisClient.Set(fmt.Sprintf(constant.TaskProgressKey, task.ID), 0, 10*time.Minute)
	resp = &prod.ImportTaskResp{Id: task.ID}
	reqID := request_key.GetRequestID(ctx)
	go func() {
		newCtx := request_key.GenerateContextWithRequestID(reqID)
		errLogs := make([]string, 0)
		skipLogs := make([]string, 0)
		defer ecode.AsyncFailOver(newCtx, "商品上传后置处理任务", conf.Conf.Env, &err)
		defer func() {
			if len(errLogs) == 0 {
				task.Extra = fmt.Sprintf("成功创建&更新%d个商品", len(data))
				task.Status = uint8(prod.TaskStatus_TASK_STATUS_SUCCESS)
				s.dao.RedisClient.Set(fmt.Sprintf(constant.TaskProgressKey, task.ID), 100, 10*time.Minute)
			} else {
				task.Status = uint8(prod.TaskStatus_TASK_STATUS_FAILED)
				s.dao.RedisClient.Set(fmt.Sprintf(constant.TaskProgressKey, task.ID), -1, 10*time.Minute)
			}
			if err = s.dao.Save(ctx, task); err != nil {
				return
			}
		}()
		parentTypeMap := make(map[string]uint32)
		prodTypeMap := make(map[string]uint32)
		for i, prodData := range data {
			var (
				parentName, prodTypeName, prodName, priceStr, groupStr, itemStr string
				ok                                                              bool
				parentId, prodTypeId                                            uint32
				skuList                                                         []*prod.CreateSkuView
			)
			s.dao.RedisClient.Set(fmt.Sprintf(constant.TaskProgressKey, task.ID),
				100*i/len(data), 10*time.Minute)
			if parentName, ok = prodData["一级分类"]; !ok {
				errLogs = append(errLogs, fmt.Sprintf(
					"第%d行一级分类字段缺失", i))
				continue
			}
			if prodTypeName, ok = prodData["二级分类"]; !ok {
				errLogs = append(errLogs, fmt.Sprintf(
					"第%d行二级分类字段缺失", i))
				continue
			}
			if prodName, ok = prodData["商品名称"]; !ok {
				errLogs = append(errLogs, fmt.Sprintf(
					"第%d行商品名称字段缺失", i))
				continue
			}
			priceStr, _ = prodData["价格"]
			groupStr, _ = prodData["规格名"]
			itemStr, _ = prodData["属性"]
			if priceStr == "" && (groupStr == "" || itemStr == "") {
				errLogs = append(errLogs, fmt.Sprintf(
					"第%d行商品名称价格为空时，规格名&属性必须都不为空", i))
				continue
			} else if priceStr != "" && (groupStr != "" || itemStr != "") {
				errLogs = append(errLogs, fmt.Sprintf(
					"第%d行商品名称价格不为空时，规格名&属性必须为空", i))
				continue
			}
			if priceStr != "" {
				// 单一规格商品
				if price, e := strconv.ParseFloat(priceStr, 64); e != nil {
					errLogs = append(errLogs, fmt.Sprintf(
						"第%d行商品名称价格%s无法转换格式", i, priceStr))
					continue
				} else {
					skuList = append(skuList, &prod.CreateSkuView{
						Price:  uint32(price * 100),
						Weight: 0,
						Items:  []*prod.SkuItemView{},
					})
				}
			} else {
				groups := strings.Split(groupStr, "，")
				itemSkus := strings.Split(itemStr, "；")
				for _, skuStr := range itemSkus {
					tmp := strings.Split(skuStr, "：")
					if len(tmp) != 2 {
						errLogs = append(errLogs, fmt.Sprintf(
							"第%d行属性格式不合法: %s", i, tmp))
						continue
					}
					if skuPrice, e := strconv.ParseFloat(tmp[1], 64); e != nil {
						errLogs = append(errLogs, fmt.Sprintf(
							"第%d行商品属性中规格价格%s无法转换格式", i, tmp[1]))
						continue
					} else {
						items := strings.Split(tmp[0], "，")
						if len(items) != len(groups) || len(items) == 0 {
							errLogs = append(errLogs, fmt.Sprintf(
								"第%d行商品属性中属性项%s与分组%s未对齐", i, tmp[1], groupStr))
							continue
						}
						skuItems := make([]*prod.SkuItemView, len(groups))
						for j, group := range groups {
							skuItems = append(skuItems, &prod.SkuItemView{
								Name: group,
								Item: items[j],
							})
						}
						skuList = append(skuList, &prod.CreateSkuView{
							Price: uint32(skuPrice * 100),
							Items: skuItems,
						})
					}
				}
			}
			if parentId, ok = parentTypeMap[parentName]; !ok {
				if prodTypeResp, e := s.CreateProdType(ctx, &prod.CreateProdTypeRequest{
					Token:    req.Token,
					PStoreId: req.PStoreId,
					Name:     parentName,
					ParentId: 0,
					Nature:   req.Nature,
				}); e != nil {
					errLogs = append(errLogs, fmt.Sprintf(
						"第%d行创建商品一级类别失败: %s", i, e.Error()))
					continue
				} else {
					parentId = prodTypeResp.Id
					parentTypeMap[parentName] = parentId
				}
			}
			if prodTypeId, ok = prodTypeMap[prodTypeName]; !ok {
				if prodTypeResp, e := s.CreateProdType(ctx, &prod.CreateProdTypeRequest{
					Token:    req.Token,
					PStoreId: req.PStoreId,
					Name:     prodTypeName,
					ParentId: parentId,
					Nature:   req.Nature,
				}); e != nil {
					errLogs = append(errLogs, fmt.Sprintf(
						"第%d行创建商品二级类别失败: %s", i, e.Error()))
					continue
				} else {
					parentId = prodTypeResp.Id
					parentTypeMap[parentName] = parentId
				}
			}
			if product, e := s.dao.GetProdInfoByName(
				ctx, prodName, req.Token.SgId, req.PStoreId); e != nil {
				isNotFound := errors.Cause(e) == gorm.ErrRecordNotFound
				if isNotFound {
					// 创建新商品
					if _, e2 := s.CreateProd(ctx, &prod.CreateProdRequest{
						Token:      req.Token,
						PStoreId:   req.PStoreId,
						Nature:     req.Nature,
						ProdTypeId: prodTypeId,
						Name:       prodName,
						Unit:       "份",
						TitlePics:  []string{},
						DetailPics: []string{},
						SkuList:    skuList,
					}); e2 != nil {
						errLogs = append(errLogs, fmt.Sprintf(
							"第%d行创建商品失败: %s", i, e2.Error()))
						continue
					}
				} else {
					errLogs = append(errLogs, fmt.Sprintf(
						"第%d行加载商品信息失败: %s", i, e.Error()))
					continue
				}
			} else if req.UpdateMode {
				// 更新商品
				if _, e2 := s.EditProd(ctx, &prod.EditProdRequest{
					Token:             req.Token,
					PStoreId:          req.PStoreId,
					Id:                product.ID,
					ProdTypeId:        prodTypeId,
					Name:              prodName,
					Phonetic:          product.Phonetic,
					Unit:              product.Unit,
					TitlePics:         product.TitlePics,
					DetailPics:        product.DetailPics,
					DetailDescription: product.Description,
					SkuList:           skuList,
				}); e2 != nil {
					errLogs = append(errLogs, fmt.Sprintf(
						"第%d行创建商品失败: %s", i, e2.Error()))
					continue
				}
			} else {
				// 跳过
				skipLogs = append(skipLogs, fmt.Sprintf(
					"第%d行商品重名， 跳过", i))
				continue
			}
		}
	}()
	return
}
